package com.pj.sso;

import com.pj.project.sys_user.SysUser;
import com.pj.util.AjaxJson;
import com.pj.util.MyHttpSessionHolder;
import com.pj.util.SaCheckTicketResult;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import jakarta.servlet.http.HttpSession;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.servlet.ModelAndView;

import java.io.IOException;
import java.util.Objects;

/**
 * SSO Client端 Controller 
 * @author click33
 */
@RestController
public class SsoClientController {

	// -------- 单点登录相关 
	
	// SSO-Client端：单点登录地址 
	@RequestMapping("/sso/login")
	public Object ssoLogin(String ticket, @RequestParam(defaultValue = "/") String back,
						   HttpServletRequest request, HttpServletResponse response) throws IOException {
		
		// 如果已经登录，则直接返回 
		HttpSession session = MyHttpSessionHolder.getSession(request);
		if(session.getAttribute("userId") != null) {
			response.sendRedirect(back);
			return null;
		}
		
		/*
		 * 此时有两种情况: 
		 * 情况1：ticket无值，说明此请求是Client端访问，需要重定向至SSO认证中心 
		 * 情况2：ticket有值，说明此请求从SSO认证中心重定向而来，需要根据ticket进行登录 
		 */
		if(ticket == null) {
			String currUrl = request.getRequestURL().toString();
			String clientLoginUrl = currUrl + "?back=" + SsoRequestUtil.encodeUrl(back);
			String serverAuthUrl = SsoRequestUtil.buildServerAuthUrl(clientLoginUrl);
			response.sendRedirect(serverAuthUrl);
			return null;
		} else {
			// 根据 ticket 登录上 
			SaCheckTicketResult ctr = SsoRequestUtil.checkTicket(ticket, request, "/sso/login");
			session.setAttribute("userId", ctr.loginId);
			session.setAttribute("user", SsoRequestUtil.getUserInfo(ctr.loginId));
			// 返回 back 地址
			response.sendRedirect(back);
			return null;
		}
	}
	
	// SSO-Client端：单点注销地址
	@RequestMapping("/sso/logout")
	public Object ssoLogout(String back, HttpServletRequest request, HttpServletResponse response) throws IOException {
		
		// 如果未登录，则无需注销 
		HttpSession session = MyHttpSessionHolder.getSession(request);
        if(session.getAttribute("userId") == null) {
			response.sendRedirect(back == null ? "/" : back);
			return null;
        }
        
        // 调用 sso-server 认证中心单点注销API 
        Object loginId = session.getAttribute("userId");  // 账号id 
		String timestamp = String.valueOf(System.currentTimeMillis());	// 时间戳 
		String nonce = SsoRequestUtil.getRandomString(20);		// 随机字符串
		String sign = SsoRequestUtil.getSign(loginId, timestamp, nonce);	// 参数签名
		
        String url = SsoRequestUtil.sloUrl + 
        		"?loginId=" + loginId +
        		"&timestamp=" + timestamp +
        		"&nonce=" + nonce +
        		"&sign=" + sign;
		if (SsoRequestUtil.isNotEmpty(SsoRequestUtil.client)) {
			url += "&client=" + SsoRequestUtil.client;
		}
        AjaxJson result = SsoRequestUtil.request(url);
        
		// 校验响应状态码，200 代表成功 
		if(result.getCode() == 200) {
			
	        // 极端场景下，sso-server 中心的单点注销可能并不会通知到此 client 端，所以这里需要再补一刀
			session.removeAttribute("userId");
			// 如果指定了 back 地址，则重定向，否则返回 JSON 信息 
			if(SsoRequestUtil.isNotEmpty(back)) {
				response.sendRedirect(back);
				return null;
			} else {
				return AjaxJson.getSuccess();
			}
			
		} else {
			// 将 sso-server 回应的消息作为异常抛出 
			throw new RuntimeException(result.getMsg());
		}
	}
	
	// SSO-Client端：单点注销回调地址
	@RequestMapping("/sso/logoutCall")
	public Object ssoLogoutCall(String autoLogout, String loginId, String timestamp, String nonce, String sign) {
		
		// 校验签名 
		String calcSign = SsoRequestUtil.getSignByLogoutCall(loginId, autoLogout, timestamp, nonce);
		if( ! calcSign.equals(sign)) {
			return AjaxJson.getError("无效签名，拒绝应答");
		}
		
		// 注销这个账号id 
		for (HttpSession session: MyHttpSessionHolder.sessionMap.values()) {
			Object userId = session.getAttribute("userId");
			if(Objects.equals(String.valueOf(userId), loginId)) {
				session.removeAttribute("userId");
				session.removeAttribute("user");
			}
		}
		
		return AjaxJson.getSuccess("账号id=" + loginId + " 注销成功");
	}

	// SSO-Client端：当前应用独自注销
	@RequestMapping("/sso/logoutByAlone")
	public Object aloneLogout(HttpServletRequest request) {
		HttpSession session = MyHttpSessionHolder.getSession(request);
		session.removeAttribute("userId");
		session.removeAttribute("user");
		return AjaxJson.getSuccess();
	}

	// -------- 访问页面相关 
	
	// SSO-Client端：首页
	@RequestMapping("/")
	public Object index(HttpServletRequest request) {
		// 判断是否登录 
		HttpSession session = MyHttpSessionHolder.getSession(request);
		boolean isLogin = session.getAttribute("userId") != null;
		request.setAttribute("isLogin", isLogin);
		if(isLogin) {
			request.setAttribute("user", session.getAttribute("user"));
		}
		return new ModelAndView("index.html");
	}
	
	// 进入info页（只有登录后才可以访问此页面）
	@RequestMapping("/info.html")
	public Object info(HttpServletResponse response, HttpServletRequest request) throws IOException {
		// 如果没有登录，去登录 
		HttpSession session = MyHttpSessionHolder.getSession(request);
		if(session.getAttribute("userId") == null) {
			String back =  SsoRequestUtil.joinParam(request.getRequestURL().toString(), request.getQueryString());
			response.sendRedirect("/sso/login?back=" + SsoRequestUtil.encodeUrl(back));
			return null;
		}
		
		return new ModelAndView("info.html");
	}

	// getInfo接口 （只有登录后才可以调用此接口）
	@RequestMapping("/getCurrInfo")
	public Object getCurrInfo(HttpServletRequest request) {
		// 如果没有登录，就返回特定信息 
		HttpSession session = MyHttpSessionHolder.getSession(request);
		if(session.getAttribute("userId") == null) {
			return AjaxJson.getNotLogin();
		}
		
		// 从Session中获取user对象 
		SysUser user = (SysUser) session.getAttribute("user");
		return AjaxJson.getSuccessData(user);
	}
	
}
